package org.zxt.web.buss.config;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.MybatisConfiguration;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import org.apache.ibatis.logging.slf4j.Slf4jImpl;
import org.apache.ibatis.mapping.Environment;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionManager;
import org.apache.ibatis.session.defaults.DefaultSqlSessionFactory;
import org.apache.ibatis.transaction.TransactionFactory;
import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory;
import org.springframework.util.StringUtils;
import org.yaml.snakeyaml.Yaml;
import org.zxt.web.context.ResourceType;
import org.zxt.web.context.mybatis.plugin.pagehelper.PageInterceptor;
import org.zxt.web.context.mybatis.plugin.pagehelper.dialect.PostgreSqlDialect;
import org.zxt.web.context.mybatis.plugin.pagehelper.parser.CountSqlParser;
import org.zxt.web.context.mybatis.plugin.pagehelper.parser.OrderByParser;

import javax.annotation.Resource;
import javax.sql.DataSource;
import java.util.Map;
import java.util.TreeMap;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Resource(type = ResourceType.Config.class)
public class MybatisPlusConfig {

    @Resource(type = ResourceType.Bean.class)
    public DataSource dataSource(Conf env){
        return new HikariDataSource(env.getWithPrefix("hirika", HikariConfig.class));
    }

    @Resource(type = ResourceType.Bean.class)
    public TransactionFactory transactionFactory(){
        return new JdbcTransactionFactory();
    }

    @Resource(type = ResourceType.Bean.class)
    public MybatisConfiguration mybatisConfiguration(DataSource dataSource, TransactionFactory transactionFactory) throws NoSuchFieldException {
        MybatisConfiguration config = new MybatisConfiguration();
        config.addMappers("org.zxt.web.buss.rep.mapper");
        config.setDefaultExecutorType(ExecutorType.SIMPLE);
        config.setEnvironment(new Environment.Builder("i")
                        .dataSource(dataSource)
                .transactionFactory(transactionFactory)
                .build());
        config.setLogImpl(Slf4jImpl.class);
        config.setDatabaseId("1");
        config.addInterceptor(new MybatisPlusInterceptor());
        config.addInterceptor(new PageInterceptor(new PostgreSqlDialect(new CountSqlParser()), new OrderByParser()));
        return config;
    }

    @Resource(type = ResourceType.Bean.class)
    public SqlSessionFactory sqlSessionFactory(MybatisConfiguration mybatisConfiguration){
        return new DefaultSqlSessionFactory(mybatisConfiguration);
    }

    @Resource(type = ResourceType.Bean.class)
    public Function<Class<?>, Object> mapperProxyTarget(SqlSessionManager sqlSessionManager){
        return (clazz)->{
            return sqlSessionManager.getConfiguration().getMapper(clazz, sqlSessionManager.openSession());
        };
    }

    @Resource(type = ResourceType.Bean.class)
    public SqlSessionManager sqlSessionManager(SqlSessionFactory sqlSessionFactory){
        return SqlSessionManager.newInstance(sqlSessionFactory);
    }
    
    private Pattern pattern = Pattern.compile("\\$\\{(.*?)\\}");

    @Resource(type = ResourceType.Bean.class)
    public Conf env(){
        Conf map = new Conf(new TreeMap<>());
        map.putAll(System.getenv());
        Map<String, Object> yamlMap = new Yaml().load(this.getClass().getResourceAsStream("/META-INF/application.yml"));
        parseEnv(yamlMap, null, (k, v)->{
            if(v instanceof String){
                Matcher matcher = pattern.matcher(((String) v));
                if(matcher.matches()) {
                    String valueRefKey = matcher.group(1);
                    String defaultVal = "";
                    if(valueRefKey.contains(":")){
                        defaultVal = valueRefKey.substring(valueRefKey.indexOf(":")+1);
                        valueRefKey = valueRefKey.substring(0, valueRefKey.indexOf(":"));
                    }
                    v = map.getOrDefault(valueRefKey, defaultVal);
                }
            }
            map.put(k, v);
        });
        return map;
    }

    private void parseEnv(Map<String, Object> tree, String parent, BiConsumer<String, Object> biConsumer){
        tree.entrySet().forEach(entry->{
            String key = StringUtils.hasText(parent) ? (parent+"."+entry.getKey()) : entry.getKey();
            Object obj = entry.getValue();
            if(obj instanceof Map){
                parseEnv((Map<String, Object>)obj, key, biConsumer);
            } else {
                biConsumer.accept(key, entry.getValue());
            }
        });
    }

    @Resource(type = ResourceType.Bean.class)
    public AuthenticationInterceptor authenticationInterceptor(){
        return new AuthenticationInterceptor();
    }

}
